package com.jingdianjichi.wx.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Redis工具类，提供了一系列操作Redis的便捷方法。
 * 使用Spring提供的RedisTemplate来进行Redis操作。
 * 包括键值对的设置和获取，有序集合的操作等。
 *
 * @author: wuyimin
 * @date: 2024/2/8
 */
@Component // 表明这是一个Spring组件，由Spring容器管理
@Slf4j // Lombok提供的日志注解，为这个类提供日志能力
public class RedisUtil {

    @Resource
    private RedisTemplate redisTemplate; // 自动注入Spring管理的RedisTemplate

    private static final String CACHE_KEY_SEPARATOR = "."; // 缓存key的分隔符

    /**
     * 构建缓存key，使用"."作为分隔符拼接字符串。
     * @param strObjs 字符串数组，这些字符串将被连接起来作为Redis的key。
     * @return 返回拼接后的字符串作为key。
     */
    public String buildKey(String... strObjs) {
        return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR));
    }

    /**
     * 检查给定key是否存在于Redis中。
     * @param key 要检查的key。
     * @return 如果存在返回true，否则返回false。
     */
    public boolean exist(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除指定的key。
     * @param key 要删除的key。
     * @return 删除成功返回true，否则返回false。
     */
    public boolean del(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 设置键值对到Redis中（不带过期时间）。
     * @param key 键。
     * @param value 值。
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置键值对到Redis中，并设置过期时间。
     * @param key 键。
     * @param value 值。
     * @param time 过期时间。
     * @param timeUnit 时间单位。
     * @return 如果key不存在且设置成功返回true，如果key已存在返回false。
     */
    public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);
    }

    /**
     * 获取指定key的值。
     * @param key 键。
     * @return 返回key对应的值。
     */
    public String get(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    // 以下是有序集合(ZSet)操作的方法

    /**
     * 向有序集合添加一个元素。
     * @param key 集合的key。
     * @param value 元素值。
     * @param score 分数。
     * @return 添加成功返回true，否则返回false。
     */
    public Boolean zAdd(String key, String value, Long score) {
        return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));
    }

    /**
     * 获取有序集合的元素数量。
     * @param key 集合的key。
     * @return 集合的元素数量。
     */
    public Long countZset(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 获取有序集合指定范围内的元素。
     * @param key 集合的key。
     * @param start 起始位置。
     * @param end 结束位置。
     * @return 位于指定范围内的元素集合。
     */
    public Set<String> rangeZset(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 从有序集合中移除指定的元素。
     * @param key 集合的key。
     * @param value 要移除的元素值。
     * @return 移除的元素数量。
     */
    public Long removeZset(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * 从有序集合中移除指定的一组元素。
     * @param key 集合的key。
     * @param value 要移除的元素值集合。
     */
    public void removeZsetList(String key, Set<String> value) {
        value.forEach(val -> redisTemplate.opsForZSet().remove(key, val));
    }

    /**
     * 获取有序集合中指定元素的分数。
     * @param key 集合的key。
     * @param value 元素值。
     * @return 元素的分数。
     */
    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 获取有序集合中指定分数范围内的元素集合。
     * @param key 集合的key。
     * @param start 分数范围起始。
     * @param end 分数范围结束。
     * @return 位于指定分数范围内的元素集合。
     */
    public Set<String> rangeByScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));
    }

    /**
     * 增加有序集合中元素的分数。
     * @param key 集合的key。
     * @param obj 元素值。
     * @param score 要增加的分数。
     * @return 增加分数后的新分数。
     */
    public Object addScore(String key, Object obj, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, obj, score);
    }

    /**
     * 获取有序集合中元素的排名（从低分到高分的索引位置）。
     * @param key 集合的key。
     * @param obj 元素值。
     * @return 元素的排名。
     */
    public Object rank(String key, Object obj) {
        return redisTemplate.opsForZSet().rank(key, obj);
    }
}
